'''
Created on 2011-12-2

@author: Administrator
'''

import logging

WAITING_TRIIGGERING, \
TRIGGERED, \
NOT_TRIGGER, \
BEGUN, \
FINISH = range(5)

INTER_FUN = set(['Success', 'Failure', 'Exception', 'NoRun'])


class NodeInfo():
    def __init__(self, name, tasktemplateid, trigger_condition):
        self.name = name
        self.trigger_condition = trigger_condition
        self.judge_code = None
        self.tasktemplateid = tasktemplateid
        self.associated_nodes = set()
        if trigger_condition != None and len(trigger_condition) != 0: 
            code = compile(trigger_condition, "", 'eval')
            names = set(code.co_names)
            self.judge_code = code
            associated_nodes = names.difference(INTER_FUN)
            if len(associated_nodes) == 0 :
                raise Exception("%s node not include any  trigger condition" % name)
            self.associated_nodes = associated_nodes


class TaskflowTemplate():
    def __init__(self, id, name, trigger_time, next_run_time, mail_reciver, nodeinfos, 
                 success_receiver=None, fail_receiver=None, except_receiver=None):
        self.id = id
        self.name = name
        self.trigger_time = trigger_time
        self.next_run_time = next_run_time
        self.mailreciver = mail_reciver
        self.node_list = {}
        for v in nodeinfos:
            self.AddNodeInfo(v.NodeName, v.TaskTemplateId, v.TriggerCondition)
        self.Check()

        self.success_mail_receiver = success_receiver or ""
        self.error_mail_receiver = fail_receiver or ""
        self.except_mail_receiver = except_receiver or ""
        
    
    def GetNextRunTime(self):    
        return self.next_run_time
    
    
    def SetNextRunTime(self, now):
        self.next_run_time = self.trigger_time.GetNextFireTime(now)
    
    
    def AddNodeInfo(self, name, tasktemplateid, trigger_condition):
        if name  in self.node_list:
            raise ValueError("%s node has same node name" % name)
        
        info = NodeInfo(name, tasktemplateid, trigger_condition)
        self.node_list[name] = info
                
        
    def Check(self):
        names = set(self.node_list.keys())
        triggernodes = set()
        for name, node in self.node_list.items():
            if not node.associated_nodes < names:
                raise ValueError("%s node trigger node not all in taskflow" % name)
            
            if name in node.associated_nodes:
                raise ValueError("%s node trigger node can't include self" % name)
            
            if not node.associated_nodes:
                triggernodes.add(name)
                if self.CheckDeadLoop(name, [name], triggernodes) == False:
                    raise ValueError("trigger path has dead loop")    
        
        if triggernodes != names:
            raise ValueError("%s node  can't be trigger" % (names - triggernodes))    
        
        
    def CheckDeadLoop(self, name, path, triggernodes):
        for k, v in self.node_list.iteritems():
            if k == name:
                continue
            if name in v.associated_nodes:
                if k in path:
                    return False
                p = path[:]
                p.append(k)
                triggernodes.add(k)
                if  self.CheckDeadLoop(k, p, triggernodes) == False:
                    return False
        return True
    
